<?php
declare (strict_types=1);

namespace app\exam\logic\admin;

use app\admin\logic\system\Log;
use app\common\exception\LogicException;
use app\exam\model\admin\QuestionPoint as QuestionPointModel;
use app\common\logic\category\Category;

/**
 * 知识点逻辑层
 */
class QuestionPoint extends QuestionPointModel
{
    /**
     * 数据列表
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2020/10/29
     *
     * @param int $pid
     * @return \think\Collection
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getList($pid = 0)
    {
        $point = $this->where('pid', $pid)->select()->append(['category_str']);

        if ($point->isEmpty()) return $point;

        foreach ($point as $c) {
            // 只是分类才有子级
            if ($pid == 0) {
                // 是否存在子级
                $c->hasChildren = $this->hasChildren($c->id);
                // 如果有子级,数量 = 子数量总和
                $c->num = $c->hasChildren ? $this->getSumNumByPid($c->id) : 0;
            }
        }
        return $point;
    }

    /**
     * 获取顶级知识点, 即知识点分类
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2020/11/23
     *
     * @return \think\Collection
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getTopList(array $map)
    {
        return $this->where('pid', 0)->where($map)->field('id as value,title')->select();
    }

    /**
     * 获取分页数据
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2020/11/23
     *
     * @param $map
     * @return \think\Paginator
     * @throws \think\db\exception\DbException
     */
    public function getPageList($map)
    {
        $list  = $this->where('pid', '<>', 0)->where($map)->field('id,title,pid')->paginate($this->getListRows());
        $cates = [];
        foreach ($list as $key => $item) {
            $pid = $item->pid;
            if (isset($cates[$pid])) {
                $cate = $cates[$pid];
            } else {
                $cate        = $this->where('id', $pid)->value('title');
                $cates[$pid] = $cate;
            }

            $item->cate = $cate;
        }

        return $list;
    }

    /**
     * 是否有子集
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2020/10/29
     *
     * @param int $pid
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function hasChildren(int $pid = 0): bool
    {
        return $this->where('pid', $pid)->find() ? true : false;
    }

    /**
     * 创建知识点
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2020/10/29
     *
     * @param array $data
     * @return bool
     */
    public function createPoint(array $data)
    {
        // 验证数据
        $this->getValidate()->scene('add')->failException()->check($data);

        if (isset($data['category'])) {
            $data['fullcategorypath'] = $data['category'];
            $data['last_category']    = end($data['category']);
        }

        if ($data['pid']??0) {
            $parent                   = $this->findOrEmpty($data['pid']);
            $category                 = $parent->fullcategorypath;
            $data['category']         = $category;
            $data['fullcategorypath'] = $category;
            $data['last_category']    = end($category);
        }

        $re = $this::create($data);
        if ($re->isEmpty()) {
            throw new LogicException('新建失败');
        }

        Log::write('新建知识点【' . $data['title'] . '】', 'exam', 'create', true);
        return $re;
    }

    /**
     * 编辑知识点
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2020/11/6
     *
     * @param int   $id
     * @param array $data
     * @return array|\think\Model
     */
    public function editPoint(int $id, array $data)
    {
        $point = $this->where('id', $id)->findOrFail();

        $check       = $data;
        $check['id'] = $id;
        // 验证数据
        $this->getValidate()->scene('edit')->failException()->check($check);

        $point->title = $data['title'];
        if (isset($data['category'])) {
            $point->category         = $data['category'];
            $point->fullcategorypath = $data['category'];
            $point->last_category    = end($data['category']);
            // 修改子知识点分类
            $this->wherePid($id)->select()->each(function ($i) use ($data)
            {
                $i->category         = $data['category'];
                $i->fullcategorypath = $data['category'];
                $i->last_category    = end($data['category']);
                $i->save();
            });
        }

        if (!$point->save()) {
            throw new LogicException('更新失败');
        }

        Log::write('更新知识点【' . $data['title'] . '】', 'exam', 'update', true);

        return $point;
    }

    /**
     * 删除知识点
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2020/10/29
     *
     * @param $id
     * @return bool
     */
    public function deletetPoint($id)
    {
        $point = $this->findOrEmpty($id);
        if ($point->isEmpty()) {
            throw new LogicException('知识点不存在');
        }
        $isCate = $point->getAttr('pid') == 0;
        // 是否为分类
        if ($isCate) {
            // 是否有子级
            if ($this->hasChildren($point->id)) {
                throw new LogicException('分类下存在知识点，不可删除');
            }
        } else if ($point->getAttr('num') > 0) {
            // 不为分类时,有试题绑定知识点
            throw new LogicException('知识点下存在试题，不可删除');
        }
        if (!$point->delete()) {
            throw new LogicException("删除失败");
        }

        Log::write('删除知识点【' . $point->title . '】', 'exam', 'delete', true);
    }

    /**
     * 知识点结构
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2020/10/29
     *
     * @param int $pid
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelQuestionPointNotFoundException
     */
    public function cascader($pid = 0, $map = [])
    {
        $cate = $this->where('pid', $pid)->where($map)->field('id as value,title as label')->select();
        if ($cate) {
            foreach ($cate as &$c) {
                $child = $this->cascader($c['value']);
                $child && $c['children'] = $child;
            }
            unset($c);
        }

        return $cate->toArray();
    }

    /**
     * 获取子类的题库数量总和
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2020/10/31
     *
     * @param int $pid
     * @return float
     */
    public function getSumNumByPid(int $pid)
    {
        return $this->where('pid', $pid)->sum('num');
    }

    /**
     * 解析知识点集合
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2020/11/25
     *
     * @param array $point
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function parseTitle(array $point = [])
    {
        $result = $this->whereIn('id', $point)->field('id,title')->select();

        return $result->toArray();
    }

    /**
     * 查询知识点,当不存在的时候会创建
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2021/3/29
     *
     * @param string $points
     * @return array
     */
    public function selectOrCreate(string $points)
    {
        if (empty($points)) {
            return [];

        }
        $points = explode('+', $points);
        static $loaded;
        $result = [];
        foreach ($points as $item) {
            if (isset($loaded[$item])) {
                $result[] = $loaded[$item];
            } else {
                // 解析出分类,知识点
                try {
                    [$cate, $point] = explode('/', $item);
                    if (!$point) {
                        continue;
                    }
                    // 查询知识点分类是否存在
                    $cateId = $this->where('title', $cate)->value('id');
                    if (!$cateId) {
                        // 创建分类
                        $model        = new static();
                        $model->title = $cate;
                        $model->save();
                        $cateId = $model->getLastInsID();
                        // 创建分类下的知识点
                        $model        = new static();
                        $model->title = $cate;
                        $model->pid   = $cateId;
                        $model->save();
                        $pointId = $model->getLastInsID();
                    } else {
                        // 查询知识点是否存在
                        $pointId = $this->where('title', $point)->where('pid', $cateId)->value('id');
                        if (!$pointId) {
                            // 创建分类下的知识点
                            $model        = new static();
                            $model->title = $point;
                            $model->pid   = $cateId;
                            $model->save();
                            $pointId = $model->getLastInsID();
                        }
                    }
                    $data          = ['id' => $pointId];
                    $loaded[$item] = $data;
                    $result[]      = $data;
                } catch (\Exception $exception) {
                }

            }
        }

        return $result;
    }

}
